#include "Avalon/IOTests/MockServerConnection.hpp"
#include "Avalon/Base/Algorithm.hpp"
#include "Avalon/IO/Channel.hpp"
#include "Avalon/IO/NotConnectedException.hpp"
#include "Avalon/IOTests/MockClientConnection.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Threading;
using namespace boost;
using namespace std;

MockServerConnection::MockServerConnection()
    : m_isOpen(false),
      m_rejectNextConnection(false),
      m_nextAccept(NULL) {}

MockServerConnection::~MockServerConnection() {
  Close();
}

void MockServerConnection::RejectNextConnection() {
  lock_guard<recursive_mutex> lock(m_mutex);
  m_rejectNextConnection = true;
}

void MockServerConnection::Open() {
  lock_guard<recursive_mutex> lock(m_mutex);
  m_isOpen = true;
}

void MockServerConnection::Accept(Out<Async<Channel*> > channel) {
  lock_guard<recursive_mutex> lock(m_mutex);
  assert(m_nextAccept == NULL);
  if(!m_isOpen) {
    BOOST_THROW_EXCEPTION(NotConnectedException());
  }
  if(m_pendingConnections.empty()) {
    m_nextAccept = channel.Get();
    return;
  }
  MockClientConnection* connection = m_pendingConnections.front();
  m_pendingConnections.erase(m_pendingConnections.begin());
  m_acceptedConnections.push_back(connection);
  connection->AcceptConnection();
  channel->SetResult(connection->CreateServerChannel());
}

bool MockServerConnection::IsOpen() const {
  lock_guard<recursive_mutex> lock(m_mutex);
  return m_isOpen;
}

void MockServerConnection::Close() {
  lock_guard<recursive_mutex> lock(m_mutex);
  if(!m_isOpen) {
    return;
  }
  for(vector<MockClientConnection*>::const_iterator i =
      m_acceptedConnections.begin(); i != m_acceptedConnections.end(); ++i) {
    (*i)->CloseConnection();
  }
  m_acceptedConnections.clear();
  for(vector<MockClientConnection*>::const_iterator i =
      m_pendingConnections.begin(); i != m_pendingConnections.end(); ++i) {
    (*i)->RejectConnection();
  }
  m_pendingConnections.clear();
  m_isOpen = false;
  if(m_nextAccept != NULL) {
    Async<Channel*>* nextAccept = m_nextAccept;
    m_nextAccept = NULL;
    nextAccept->SetException(NotConnectedException());
  }
  if(m_closedSlot == NULL) {
    return;
  }
  shared_ptr<ClosedSlot> closedSlot = m_closedSlot;
  (*closedSlot)();
}

void MockServerConnection::SetClosedSlot(const ClosedSlot& slot,
    Out<bool> isOpen) {
  lock_guard<recursive_mutex> lock(m_mutex);
  *isOpen = m_isOpen;
  m_closedSlot.reset(new ClosedSlot(slot));
}

bool MockServerConnection::AddPendingConnection(
    MockClientConnection* connection) {
  lock_guard<recursive_mutex> lock(m_mutex);
  if(!m_isOpen) {
    connection->RejectConnection();
    return true;
  } else if(m_rejectNextConnection) {
    connection->RejectConnection();
    m_rejectNextConnection = false;
    return true;
  } else if(m_nextAccept == NULL) {
    m_pendingConnections.push_back(connection);
    return false;
  } else {
    m_acceptedConnections.push_back(connection);
    connection->AcceptConnection();
    Async<Channel*>* nextAccept = m_nextAccept;
    m_nextAccept = NULL;
    nextAccept->SetResult(connection->CreateServerChannel());
    return true;
  }
}

void MockServerConnection::RemoveConnection(MockClientConnection* connection) {
  lock_guard<recursive_mutex> lock(m_mutex);
  Remove(m_acceptedConnections, connection);
}
